#include "Socket.h"
#include "string.h"
#include <string.h>
#include <errno.h>
#include <fcntl.h>

Socket::Socket() :
		m_sock(-1) {

	memset(&m_addr, 0, sizeof(m_addr));
	logger = new Logger;
}

Socket::~Socket() {
	if (is_valid())
		::close(m_sock);
	delete logger;
}

void Socket::closeSock() {
	if (is_valid()){
		::close(m_sock);
		//::shutdown(m_sock,1);
	}
}

bool Socket::create() {
	//SOCK_STREAM 0
	// SOCK_DGRAM IPPROTO_UDP
	m_sock = socket(AF_INET, SOCK_STREAM, 0);

	if (!is_valid()) {

		puts(strerror(errno));
		HAL_TRACE_CORE(logger, " create Socket no es valido m_sock %s \n", strerror(errno));
		return false;
	}

	int on = 0;


	if (setsockopt(m_sock, SOL_SOCKET, SO_REUSEADDR, (const char*) &on, sizeof(on)) == -1) {
		puts(strerror(errno));
		HAL_TRACE_CORE(logger, " setsockopt Socket SO_REUSEADDR %s \n", strerror(errno));
		return false;
	}

//	const char* optval2 = "eth1"; // 4 bytes long, so 4, below:
//	if (setsockopt(m_sock, SOL_SOCKET, SO_BINDTODEVICE, optval2, 4) == -1){
//		puts(strerror(errno));
//		HAL_TRACE_CORE(logger, " setsockopt Socket SO_BINDTODEVICE %s \n", strerror(errno));
//
//		return false;
//	}


	return true;

}

bool Socket::bind(const int port) {

	if (!is_valid()) {
		return false;
	}

	m_addr.sin_family = AF_INET;
	m_addr.sin_addr.s_addr = INADDR_ANY;
	m_addr.sin_port = htons(port);

	int bind_return = ::bind(m_sock, (struct sockaddr *) &m_addr, sizeof(m_addr));

	if (bind_return == -1) {
		puts(strerror(errno));
		HAL_TRACE_CORE(logger, " bind Socket  %s \n", strerror(errno));
		return false;
	}

	return true;
}

bool Socket::listen() const {
	if (!is_valid()) {
		return false;
	}

	int listen_return = ::listen(m_sock, MAXCONNECTIONS);

	if (listen_return == -1) {
		puts(strerror(errno));
		HAL_TRACE_CORE(logger, " listen Socket  %s \n", strerror(errno));
		return false;
	}

	return true;
}

bool Socket::accept(Socket& new_socket) const {

	int addr_length = sizeof(sockaddr_in);
	new_socket.m_sock = ::accept(m_sock, (sockaddr *) &(new_socket.m_addr), (socklen_t *) &addr_length);

	if (new_socket.m_sock <= 0) {
		puts(strerror(errno));
		HAL_TRACE_CORE(logger, " accept Socket  %s \n", strerror(errno));
		return false;

	} else {
		return true;
	}
}

bool Socket::send(const std::string s) const {

	int status = ::send(m_sock, s.c_str(), s.size(), MSG_NOSIGNAL);

	if (status == -1) {

		puts(strerror(errno));
		HAL_TRACE_CORE(logger, " send Socket  %s \n", strerror(errno));

		return false;

	} else {
		return true;
	}
}

int Socket::recv(std::string& s) const {
	char buf[MAXRECV + 1 ];

s	= "";

	memset ( buf, 0, MAXRECV + 1 );



	char buffer[1000];
	int recvCount=0;
	while ( (recvCount = ::recv(m_sock, buffer, sizeof(buffer), 0 ) ) > 0 )
	{

		if ( recvCount == -1 )
			{
				puts(strerror(errno));
				HAL_TRACE_CORE(logger," recv Socket  %s \n",strerror(errno) );
				return 0;
			}
			else if ( recvCount == 0 )
			{
				puts(strerror(errno));
				HAL_TRACE_CORE(logger," recv Socket  %s \n",strerror(errno) );
				return 0;
			}


	  s.append( buffer, recvCount );
	}

	return 1;
//
//
//	int status = ::recv ( m_sock, buf, MAXRECV, 0 );
////http://developerweb.net/viewtopic.php?id=5163
//	if ( status == -1 )
//	{
//		puts(strerror(errno));
//		HAL_TRACE_CORE(logger," recv Socket  %s \n",strerror(errno) );
//		//cout << "status == -1   errno == " << errno << "  in Socket::recv\n";
//		return 0;
//	}
//	else if ( status == 0 )
//	{
//		puts(strerror(errno));
//		HAL_TRACE_CORE(logger," recv Socket  %s \n",strerror(errno) );
//		return 0;
//	}
//	else
//	{
//		s = buf;
//		return status;
//	}
}


/*int srecv(int sockfd, void *buf) {
    int ret, len, i, j, bufsz;
    char *bufp, *p, *delim;

    bufsz = 8000;
    delim = "\n";

    if (delim == NULL) delim = "";
    len = strlen (delim);
    if (len < 1) len = 1;

    for (ret = 0, bufp = buf; ret < bufsz; ) {

        do {
            i = recv (sockfd, bufp, bufsz - ret, MSG_PEEK);
        } while ((i < 0) && (errno == EINTR));

        if (i < 0) {
            ret = i; break;
        } else if (i == 0) {
            break;
        }

        for (p = memchr (bufp, *delim, i); p; ) {
            if ((len > 1) && memcmp (p, delim, len)) {
                p++; j = i - (p - bufp);
                if (j > 0) p = memchr (p, *delim, j);
                else p = NULL;
            } else {
                p += len; i = p - bufp; break;
            }
        }

        do {
            i = recv (sockfd, bufp, i, 0);
        } while ((i < 0) && (errno == EINTR));


        if (i < 0) {
            ret = i; break;
        } else if (i == 0) {
            break;
        } else {
            bufp += i; ret += i;
            if (p) break;
        }

    }
    return (ret);
}*/




bool Socket::connect(const std::string host, const int port) {
	if (!is_valid())
		return false;

	m_addr.sin_family = AF_INET;
	m_addr.sin_port = htons(port);

	int status = inet_pton(AF_INET, host.c_str(), &m_addr.sin_addr);

	if (errno == EAFNOSUPPORT) {
		puts(strerror(errno));
		HAL_TRACE_CORE(logger, " inet_pton Socket  %s \n", strerror(errno));
		return false;
	}
	status = ::connect(m_sock, (sockaddr *) &m_addr, sizeof(m_addr));

	if (status == 0) {
		return true;
	} else {
		puts(strerror(errno));
		HAL_TRACE_CORE(logger, " recv Socket  %s \n", strerror(errno));
		return false;
	}
}

void Socket::set_non_blocking(const bool b) {

	int opts;

	opts = fcntl(m_sock, F_GETFL);

	if (opts < 0) {
		return;
	}

	if (b)
		opts = (opts | O_NONBLOCK );
	else
		opts = (opts & ~O_NONBLOCK );

	fcntl(m_sock, F_SETFL, opts);

}
